﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using OpenNETCF.MTConnect;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace VirtualAgentInfoAdapter
{
    class VirtualAgentDevice : HostedDeviceBase
    {
        private DateTime m_startTime;

        private InfoAdapter Adapter { get; set; }

        public VirtualAgentDevice(InfoAdapter adapter) : base(adapter)
        {
            m_startTime = DateTime.Now;
            Adapter = adapter;
        }

        public override IEnumerable<IHostedComponent> Components
        {
            get
            {
                return null;
            }
        }

        [EventDataItem]
        public string VersionNumber
        {
            get { return Adapter.AgentInterface.Version; }
        }

        [EventDataItem]
        public double DataRate
        {
            get
            {
                TimeSpan diff = DateTime.Now - m_startTime;
                if (diff.TotalMinutes == 0) return 0;
                return BufferCount / diff.TotalMinutes;
            }
        }

        [EventDataItem]
        public long BufferSize
        {
            get
            {
                return Adapter.AgentInterface.BufferSize;
            }
        }

        [EventDataItem]
        public long BufferCount
        {
            get
            {
                return Adapter.AgentInterface.BufferCount;
            }
        }

        [EventDataItem]
        public string UpTime
        {
            get
            {
                var upTime = DateTime.Now - m_startTime;
                return (string.Format("{0}.{1:00}:{2:00}:{3:00}",
                        upTime.Days,
                        upTime.Hours,
                        upTime.Minutes,
                        upTime.Seconds));
            }
        }

        [EventDataItem(ItemType = DataItemType.CLOCK_TIME)]
        public string AgentDateTime
        {
            get
            {
                return DateTime.Now.ToString();
            }
        }

        private int m_startAvailable = 0;
        private int m_lastLoss = 0;

        [Conditional("DEBUG")]
        private void MemoryUpdate(int currentAvailable)
        {
            if (m_startAvailable == 0)
            {
                m_startAvailable = currentAvailable;
                return;
            }

            var loss = m_startAvailable - currentAvailable;

            if (loss <= m_lastLoss) return;
            var lossPerMinute = loss / ((DateTime.Now - m_startTime).TotalMinutes);
            Debug.WriteLineIf(loss > 0, string.Format("Memory loss is {0} ({1} b/min)", loss, lossPerMinute));
            m_lastLoss = loss;
        }

        [EventDataItem]
        public int AvailableMemory
        {
            get
            {
                if (Environment.OSVersion.Platform == PlatformID.WinCE)
                {
                    MEMORYSTATUS ms = new MEMORYSTATUS();
                    GlobalMemoryStatusCF(ref ms);
                    MemoryUpdate(ms.dwAvailPhys);
                    return ms.dwAvailPhys;
                }

                MEMORYSTATUSEX msx = new MEMORYSTATUSEX();
                msx.dwLength = Marshal.SizeOf(msx);
                GlobalMemoryStatusFFx(ref msx);
                return (int)msx.dwAvailPhys;                
            }
        }

        [EventDataItem]
        public int MemoryLoad
        {
            get
            {
                if (Environment.OSVersion.Platform == PlatformID.WinCE)
                {
                    MEMORYSTATUS ms = new MEMORYSTATUS();
                    GlobalMemoryStatusCF(ref ms);
                    return ms.dwMemoryLoad;
                }

                MEMORYSTATUSEX msx = new MEMORYSTATUSEX();
                GlobalMemoryStatusFFx(ref msx);
                return msx.dwMemoryLoad;
            }
        }

        private struct MEMORYSTATUS
        {
            public int dwLength;
            public int dwMemoryLoad;
            public int dwTotalPhys;
            public int dwAvailPhys;
            public int dwTotalPageFile;
            public int dwAvailPageFile;
            public int dwTotalVirtual;
            public int dwAvailVirtual;
        } ;

        private struct MEMORYSTATUSEX
        {
            public int dwLength;
            public int dwMemoryLoad;
            public ulong dwTotalPhys;
            public ulong dwAvailPhys;
            public ulong dwTotalPageFile;
            public ulong dwAvailPageFile;
            public ulong dwTotalVirtual;
            public ulong dwAvailVirtual;
            public ulong dwAvailExtendedVirtual;
        } ;

        [DllImport("coredll", EntryPoint = "GlobalMemoryStatus", SetLastError = true)]
        static extern void GlobalMemoryStatusCF(ref MEMORYSTATUS lpBuffer);

        [DllImport("kernel32", EntryPoint = "GlobalMemoryStatusEx", SetLastError = true)]
        static extern void GlobalMemoryStatusFFx(ref MEMORYSTATUSEX lpBuffer);
    }
}
